package com.hm.business.shorturl.service.impl;

import com.hm.business.shorturl.domain.ShortUrlDTO;
import com.hm.business.shorturl.dto.LinkUrlReqDTO;
import com.hm.business.shorturl.dto.ShortUrlResDTO;
import com.hm.business.shorturl.mapper.ShortUrlMapper;
import com.hm.business.shorturl.service.NumberService;
import com.hm.business.shorturl.service.ShortUrlService;
import com.hm.business.vo.ResultVO;
import com.hm.common.config.ShortUrlConfig;
import com.hm.common.config.ShortUrlConstants;
import com.hm.common.utils.PartitionUtil;
import com.hm.common.utils.RedisUtils;
import com.hm.common.utils.UrlUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* @Description: ShortUrl实现层
* @param 
* @return 
* @throws
* @author Coder编程
* @date 2021/6/16 16:36
*/
@Slf4j
@Service
public class ShortUrlServiceImpl implements ShortUrlService {

    @Autowired
    private ShortUrlMapper shortUrlMapper;

    @Autowired
    private ShortUrlConfig shortUrlConfig;
    /**分布式锁的key*/
    private static String LOCK_KEY = "shortUrl";


    @Autowired
    private Redisson redisson;

    @Autowired
    private NumberService numberService;


    /**
     * 生成 短链接
     */
    @Override
    public ResultVO<ShortUrlResDTO> generateShortUrl(LinkUrlReqDTO linkUrlReqDTO) {

        /* 如果相同长链短时间内多次请求,会先走缓存 */
        String code = DigestUtils.md5DigestAsHex(linkUrlReqDTO.getLongUrl().getBytes());


        //TODO 查找redis缓存
        RLock lock = redisson.getLock(LOCK_KEY);
        try{
            Future<Boolean> res = lock.tryLockAsync(100, 5, TimeUnit.SECONDS);
            boolean flag = res.get();
            if(flag){
                //没有缓存走接下来的逻辑
                /* 通过平台来查找分库字段值 */
                int dbShare = PartitionUtil.getPlatForm(linkUrlReqDTO.getChannelName());
                Long number = numberService.getNumber(linkUrlReqDTO.getChannelName());
                if (number == null) {
                    /* 发号失败,直接返回 */
                    return new ResultVO(500,"发号失败！");
                }
                ShortUrlDTO urlDTO = new ShortUrlDTO();
                BeanUtils.copyProperties(linkUrlReqDTO, urlDTO);
                /* 生成短链 */
                String shortUrl = dbShare + UrlUtil.to62RadixString(number);
                String shortUrlWithDomainName = shortUrlConfig.getShortDomainName() + shortUrl;
                urlDTO.setDbShare(dbShare);
                urlDTO.setTableShare(number);
                urlDTO.setShortUrl(shortUrl);
                urlDTO.setCode(code);
                /*  插入数据库 */
                int insertFlag = shortUrlMapper.insert(urlDTO);

                if (insertFlag != 1) {
                    return null;
                }

                /* 把带上域名的短链返回 */
                urlDTO.setShortUrl(shortUrlWithDomainName);
                ShortUrlResDTO result = new ShortUrlResDTO();
                BeanUtils.copyProperties(urlDTO, result);
                /* 存到缓存 */
                String redisKey = RedisUtils.getRedisKey(number, shortUrlConfig.getShortUrlRedisKeyPrefix(), String.valueOf(dbShare));
                RedisUtils.getDataFromRedisOrNot(redisKey, ShortUrlDTO.class, ShortUrlConstants.SHORT_URL_TIME_OUT, TimeUnit.HOURS, () -> urlDTO, null);

                return ResultVO.getSuccess(result);
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally{
            lock.unlock(); //释放锁
        }
        return null;
    }



    @Override
    public ResultVO<String> queryShortUrl(Integer dbShare, String realShortUrl) {
        Boolean checkPlatForm = PartitionUtil.checkPlatForm(dbShare);
        if (!checkPlatForm) {
            return new ResultVO(400,"不正常的短链");
        }
        Long tableShare = UrlUtil.radixString(realShortUrl);
        /* 还原短链接 至 长网址 */
        String redisKey = RedisUtils.getRedisKey(tableShare, shortUrlConfig.getShortUrlRedisKeyPrefix(), String.valueOf(dbShare));

        ShortUrlDTO dataFromRedisOrNot = RedisUtils.getDataFromRedisOrNot(redisKey, ShortUrlDTO.class, ShortUrlConstants.SHORT_URL_TIME_OUT, TimeUnit.HOURS, () -> {
            /* 从数据库中查找长链 */
            ShortUrlDTO shortUrlDTO = shortUrlMapper.queryUrl(dbShare + realShortUrl, dbShare, tableShare);
            return shortUrlDTO;
        }, b -> b != null);
        if (!shortUrlStatusCheck(dataFromRedisOrNot)) {
            return new ResultVO(400,"不正常的短链");
        } else {
            return ResultVO.getSuccess(dataFromRedisOrNot.getLongUrl());
        }
    }

    /**
     * 正常url返回true ,非正常返回false
     *
     * @param shortUrlDTO url
     * @return
     */
    private boolean shortUrlStatusCheck(ShortUrlDTO shortUrlDTO) {
        boolean isErrorUrl = shortUrlDTO == null || shortUrlDTO.getActiveState() == 0 || LocalDateTime.parse(shortUrlDTO.getExpirationDate().replace(" ", "T")).isBefore(LocalDateTime.now());
        return !isErrorUrl;
    }

}
